home *** CD-ROM | disk | FTP | other *** search
/ BCI NET 2 / BCI NET 2.iso / archives / programming / source / xdme_1.84_src.lha / XDME / Src / command.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-12-05  |  25.1 KB  |  1,104 lines

  1. /******************************************************************************
  2.  
  3.     MODUL
  4.     $Id: command.c 1.3 1994/09/09 12:31:30 digulla Exp digulla $
  5.  
  6.     HISTORY
  7.     06. Dec 1992    ada created
  8.     $Log: command.c $
  9.  * Revision 1.3  1994/09/09  12:31:30  digulla
  10.  * added new style Prototypes, DEFCMD and DEFHELP
  11.  *
  12.  * Revision 1.2  1994/08/19  14:08:50  digulla
  13.  * added new commands by B. Noll
  14.  *
  15.  * Revision 1.1  1994/08/13  16:35:34  digulla
  16.  * Initial revision
  17.  *
  18.  
  19. ******************************************************************************/
  20.  
  21. /**************************************
  22.         Includes
  23. **************************************/
  24. #include "defs.h"
  25.  
  26.  
  27. /**************************************
  28.         Globale Variable
  29. **************************************/
  30. extern int foundcmd;       /* control for implicit ARexx macro invocation   */
  31. extern int cmderr;       /* global command error flag for do_rexx()'s use */
  32.  
  33.  
  34. /**************************************
  35.       Interne Defines & Strukturen
  36. **************************************/
  37. #define CF_COK    1   /*    Can be executed while in command line mode    */
  38. #define CF_PAR    2   /*    ESCIMM special flag.. save rest of command line */
  39.             /*    so it can be executed after user entry        */
  40. #define CF_ICO    4   /*    OK to execute if iconified, else uniconify first*/
  41. #define CF_VWM    8   /*    OK to execute if in viewmode, else abort    */
  42. #define CF_BLK 16   /*    needs Block */
  43.  
  44. #define BTOCP(val, type)    ((type)((long)val << 2))
  45.  
  46. typedef void (*FPTR)(long);
  47.  
  48. typedef struct
  49. {
  50.     const char * name;    /* command name     */
  51.     UBYTE args;     /* number of arguments    */
  52.     UBYTE flags;    /* flags        */
  53.     FPTR func;        /* c-function        */
  54. } COMM;
  55.  
  56. #define MAXIA        5
  57.  
  58.  
  59. /**************************************
  60.         Interne Variable
  61. **************************************/
  62.  
  63. static CONST COMM Comm[] =
  64. {
  65. #undef DEFUSERCMD
  66. #define DEFUSERCMD(str,nargs,flags,ret,func,param,ext)  str, nargs, flags, (FPTR) func,
  67. #include "commands.h"
  68. #undef DEFUSERCMD
  69. #define DEFUSERCMD(str,nargs,flags,ret,func,param,ext)  ret func param ext
  70. };
  71.  
  72.  
  73. static CONST COMM repcmd =
  74. {
  75.     "repeat",        2, CF_COK|CF_ICO, (FPTR)do_repeat,
  76. };
  77.  
  78.  
  79. /**************************************
  80.        Interne Prototypes
  81. **************************************/
  82.  
  83.  
  84. Prototype int do_command (char * str);
  85.  
  86. int do_command (char * str)
  87. {
  88.     char     * arg;
  89.     char     * aux1,
  90.          * aux2,
  91.          * repstr[MAXIA];
  92.     char       quoted;
  93.     WORD       repi = 0;
  94.     long       j;
  95.     WORD       low;
  96.     WORD       high;
  97.     WORD       diff;
  98.     static int level = 0;
  99.     COMM     * comm;
  100. #define DEBUG_FILE  /* Logging on ? */
  101. #ifdef DEBUG_FILE
  102.     static FILE * debug_file = NULL;
  103.  
  104.     if (!debug_file && GETF_DEBUG(Ep))
  105.     debug_file = fopen ("t:XDME.debug", "w");
  106. #endif
  107.  
  108.     if (!str || !*str)
  109.     return (1);
  110.  
  111. #ifdef DEBUG_FILE
  112.     if (debug_file && GETF_DEBUG(Ep))
  113.     fprintf (debug_file, "%s\n", str);
  114. #endif
  115.  
  116. if (GETF_DEBUG(Ep)) printf("dcom[%2d|%d]: %s\n", level+1, GETF_ABORTCOMMAND(Ep), str);
  117.  
  118.     if (MacroRecord != 0 && level == 0) {       /* PATCH_NULL [14 Feb 1993] : added */
  119.     add_record (str);                       /* PATCH_NULL [14 Feb 1993] : added */
  120.     } /* if */                    /* PATCH_NULL [14 Feb 1993] : added */
  121.  
  122.     if (++level > 20)
  123.     {
  124. DEFMESSAGE( _COM_recursion_too_deep, "command:\nRecursion too deep" )
  125.     error (_COM_recursion_too_deep);
  126.     --level;
  127.  
  128.     foundcmd = 1;    /* to prevent us from trying an ARexx macro */
  129.  
  130.     return (0);
  131.     }
  132.  
  133.     while ((arg = breakout (&str, "ed, &aux1)) &&
  134.         !(GETF_LOOPBREAK(Ep) || GETF_LOOPCONT(Ep)) )
  135.     {
  136. if (GETF_DEBUG(Ep)) printf("    [%2d|%d] CHECK %s\n",level, GETF_ABORTCOMMAND(Ep), arg);
  137.     if (quoted)
  138.     {
  139. if (GETF_DEBUG(Ep)) printf("    [%2d|%d] write %s\n",level, GETF_ABORTCOMMAND(Ep), arg);
  140.         if (GETF_VIEWMODE(Ep) && !GETF_COMLINEMODE(Ep))
  141.         {
  142. if (GETF_DEBUG(Ep)) printf("-- viewmode");
  143.         goto fail;
  144.         } /* if */
  145.  
  146.         if (GETF_ICONMODE(Ep))
  147.         uniconify ();
  148.  
  149.         if ((ActualBlock.ep == Ep) && GETF_KILLBLOCK(Ep) && !GETF_COMLINEMODE(Ep))
  150.         do_bdelete();
  151.  
  152.         text_write (arg);
  153.         goto loop;
  154.     } /* if (quoted) */
  155.  
  156.     if (isalpha (*arg) || (*arg == '_'))
  157.     {
  158.         low  = 0;
  159.         high = sizeof(Comm)/sizeof(Comm[0]) - 1;
  160.  
  161.         do
  162.         {
  163.         j = (low + high)/2;
  164.  
  165.         diff = stricmp (arg, Comm[j].name);
  166.  
  167.         if (!diff)
  168.             break;
  169.  
  170.         if (diff < 0)
  171.             high = j-1;
  172.         else
  173.             low = j+1;
  174.         } while (low <= high);
  175.  
  176.         if (!diff)
  177.         {
  178. if (GETF_DEBUG(Ep)) printf("    [%2d|%d] comm? %s\n",level, GETF_ABORTCOMMAND(Ep), arg);
  179.  
  180.         comm = &Comm[j];
  181.  
  182.         foundcmd = 1;
  183.  
  184.         av[0] = (UBYTE *)comm->name;
  185.  
  186.         for (j=1; j<=comm->args; j++)
  187.         {
  188.             av[j] = (UBYTE *)breakout (&str, "ed, &aux2);
  189.  
  190.             if (aux2)
  191.             {
  192.             if (repi == MAXIA)
  193.             {
  194.                 free (aux2);
  195.  
  196. DEFMESSAGE( _COM_too_many_Cargs_for, "command:\nToo many args for\n%s" )
  197.                 error (_COM_too_many_Cargs_for, av[0]);
  198.  
  199. if (GETF_DEBUG(Ep)) printf("-- varargs Oflow");
  200.  
  201.                 goto fail;
  202.             } else
  203.             {
  204.                 repstr[repi ++] = aux2;
  205.             }
  206.             }
  207.  
  208.             if (!av[j])
  209.             {
  210.             if (j < 2)
  211. DEFMESSAGE( _COM_bad_Cargument_of, "command:\nBad argument %ld of\n%s" )
  212.                 error (_COM_bad_Cargument_of, j, av[0]);
  213.             else
  214. DEFMESSAGE( _COM_bad_Cargument_of_2, "command:\nBad argument %ld of\n%s %s ..." )
  215.                 error (_COM_bad_Cargument_of_2, j, av[0], av[1]);
  216.  
  217. if (GETF_DEBUG(Ep)) printf("-- less args");
  218.             goto fail;
  219.             }
  220.         } /* for all args */
  221.  
  222.         av[j] = NULL;    /* end of arglist */
  223.  
  224. process:
  225.         if ((comm->flags & CF_COK) || !GETF_COMLINEMODE(Ep))
  226.         {
  227.             if (GETF_VIEWMODE(Ep) && !GETF_COMLINEMODE(Ep) && !(comm->flags & CF_VWM))   /* PATCH_NULL */
  228.             {
  229. if (GETF_DEBUG(Ep)) printf("-- viewmode");
  230.             goto fail;                    /* PATCH_NULL */
  231.             } /* if */                        /* PATCH_NULL */
  232.  
  233.             if (comm->flags & CF_PAR)
  234.             {
  235.             if (Partial)
  236.                 free (Partial);
  237.  
  238.             Partial = (char *)malloc (strlen (str) + 1);
  239.             strcpy (Partial, str);
  240.             str += strlen(str);     /*  skip string */
  241.             } /* if CF_PAR */
  242.  
  243.             if (GETF_ICONMODE(Ep) && !(comm->flags & CF_ICO))
  244.             {
  245. //printf ("* deiconify because off %s\n", av[0]);
  246.             uniconify ();
  247.             }
  248.  
  249.             if ((ActualBlock.ep == Ep) && GETF_KILLBLOCK(Ep) && !GETF_COMLINEMODE(Ep) && !(comm->flags & CF_BLK))
  250.             do_bdelete();
  251.  
  252. if (GETF_DEBUG(Ep)) printf ("    [%2d|%d] comm: %s/%s\n", level, GETF_ABORTCOMMAND(Ep), av[0], av[1]);
  253.  
  254.             (*comm->func)(-1);
  255.         } /* if CF_COK or Not Comlinemode */
  256.  
  257.         if (GETF_ABORTCOMMAND(Ep))
  258.         {
  259. if (GETF_DEBUG(Ep)) printf("-- aborted");
  260.             goto fail;
  261.         }
  262.  
  263.         goto loop;
  264.         } /* if arg == command */
  265.     } else if (isdigit(*arg))
  266.     {
  267.         av[0] = "repeat";
  268.         av[1] = arg;
  269.         av[2] = (UBYTE *)breakout(&str, "ed, &aux2);
  270.  
  271.         if (aux2)
  272.         {
  273.         repstr[repi ++] = aux2;
  274.         }
  275. if (GETF_DEBUG(Ep)) printf("-- repeat");
  276.  
  277.         if (!av[2])
  278.         {
  279. DEFMESSAGE( _COM_bad_2nd_repeat_arg, "command:\nBad second argument\nfor short-repeat" )
  280.         error (_COM_bad_2nd_repeat_arg);
  281.         goto fail;
  282.         }
  283.  
  284.         av[3] = 0;
  285.         comm = &repcmd;
  286.  
  287.         goto process;
  288.     } /* Repeat */
  289.  
  290.  
  291. #ifdef PATCH_ALIAS
  292.     {
  293.         /*
  294.         **    Search for a macro - we had to put that scan
  295.         **    before the checks for menus and keys, as
  296.         **    keys are only checked up to 3 chars
  297.         */
  298.  
  299.         void * macro;
  300.         int    ret;
  301.  
  302.         if (macro = (void*)getmacro(arg))
  303.         {
  304.         int narg = nummacroargs (macro);
  305.  
  306.         /* av[0] = (CHAR *)((NODE*)macro)->ln_Name; */
  307. if (GETF_DEBUG(Ep)) printf("    [%2d|%d] macro? %s\n",level, GETF_ABORTCOMMAND(Ep), arg);
  308.  
  309.         av[0] = NULL;
  310.  
  311.         foundcmd = 1;
  312.  
  313.         for (j = 1; j <= narg; ++j)
  314.         {
  315.             av[j] = (UBYTE *)breakout (&str, "ed, &aux2);
  316.  
  317.             if (aux2)
  318.             {
  319.             if (repi == MAXIA)
  320.             {
  321.                 free(aux2);
  322. DEFMESSAGE( _COM_too_many_Margs_for, "macro:\nToo many args for\n%s" )
  323.                 error (_COM_too_many_Margs_for, av[0]);
  324. if (GETF_DEBUG(Ep)) printf("-- M:varargs Oflow");
  325.                 goto fail;
  326.             } else
  327.             {
  328.                 repstr[repi++] = aux2;
  329.             }
  330.             }
  331.  
  332.             if (!av[j])
  333.             {
  334. DEFMESSAGE( _COM_bad_Margument_of, "macro:\nBad argument %ld of\n%s" )
  335.             error (_COM_bad_Margument_of, j, av[0]);
  336. if (GETF_DEBUG(Ep)) printf("-- M:less args");
  337.             goto fail;
  338.             }
  339.         }
  340.  
  341.         av[j] = NULL;    /* end of arglist */
  342.  
  343. if (GETF_DEBUG(Ep)) printf ("    [%2d|%d] macro: %s/%s\n", level, GETF_ABORTCOMMAND(Ep), arg, av[1]);
  344.         ret = callmacro(macro);
  345.  
  346.         if (!ret)
  347.         {
  348. if (GETF_DEBUG(Ep)) printf("-- M:abort");
  349.             goto fail;
  350.         } /* if no succ */
  351.  
  352.         goto loop;
  353.         } /* if */
  354.     } /* block */
  355. #endif /* PATCH_ALIAS */
  356.  
  357.  
  358.     /* Command not found, check for macro    */
  359.     {
  360.         char * cmdstr;
  361.         int    ret;
  362.  
  363.         if ((cmdstr = keyspectomacro(arg)) || (cmdstr = menutomacro(arg)))
  364.         {
  365.         cmdstr = strdup (cmdstr);
  366.  
  367. if (GETF_DEBUG(Ep)) printf ("    [%2d|%d] key/menu %s\n", level, GETF_ABORTCOMMAND(Ep), cmdstr);
  368.  
  369.         ret = do_command (cmdstr);
  370.  
  371.         free (cmdstr);
  372.  
  373.         if (ret)
  374.         {
  375.             foundcmd = 1;
  376.             goto loop;
  377.         }
  378.  
  379. if (GETF_DEBUG(Ep)) printf("-- K:abort");
  380.  
  381.         goto fail;
  382.         } /* if arg is macro or menu */
  383.     }
  384.  
  385.  
  386. /*
  387. *!  PATCH_JUX (reine gaudi)
  388. *!
  389. *!    Direct assignments of existing vars
  390. *! With that patch something is possible like:
  391. *!    set abc 1 ... abc = 3 (instead of set abc 3)
  392. *!
  393. *! if arg1 is a "=" we search for a variable of the name in arg0
  394. *! if there is such a variable, we assign its value to the value
  395. *! in arg2  (the correct types are used)
  396. *!
  397. *!  But what shall we do, if there was not such a variable???
  398. */
  399.     {
  400.         char * dummy = str;
  401.         int    type  = VAR_NEX;
  402.  
  403.         while (*dummy && *dummy <33)
  404.         {
  405.         dummy ++;
  406.         } /* while */
  407.  
  408.         if (*dummy == '=')
  409.         {
  410.         dummy ++;
  411.  
  412.         str = dummy;
  413.  
  414.         dummy = GetTypedVar (arg, &type);
  415.  
  416.         if (dummy)
  417.         {
  418.             free (dummy);
  419.  
  420.             dummy = breakout (&str, "ed, &aux2);
  421.  
  422.             if (!dummy)
  423.             {
  424. DEFMESSAGE( _COM_assign_wo_value, "command:\nAssignment without value" )
  425.             error (_COM_assign_wo_value);
  426.             goto fail;
  427.             } /* if */
  428.  
  429.             SetTypedVar (arg, dummy, type);
  430.  
  431.             if (aux2)
  432.             {
  433.             free (aux2);
  434.             } /* if */
  435.  
  436.             goto loop;
  437.         } else
  438.         { /* there is no variable of the given name  - WAS sollen wir jetzt machen ? einen Default verwenden ??? */
  439.  
  440.         } /* if */
  441.         } /* if */
  442.     } /* block direct assignment */
  443.  
  444.     /* Command still not found, check for public macro  */
  445.     /* code to be added */
  446.  
  447.     if (do_rxImplied (arg, str))
  448. DEFMESSAGE( _COM_unknown_command, "command:\nUnknown command\n`%s'" )
  449.         error (_COM_unknown_command, arg);
  450.  
  451. if (GETF_DEBUG(Ep)) printf("running into fail");
  452.  
  453. fail:
  454. if (GETF_DEBUG(Ep)) printf ("fail[%2d|%d]\n", level, GETF_ABORTCOMMAND(Ep));
  455.     --level;
  456.  
  457.     while (--repi >= 0)
  458.         free (repstr[repi]);
  459.  
  460.     if (aux1) free (aux1);
  461.  
  462.     return(0);
  463.  
  464. loop:
  465.     if (aux1) free (aux1);
  466.  
  467.     // aux1 = NULL;      /* PATCH_BREAK - is not necessary */
  468.     } /* while (arg) */
  469.  
  470. /* ret_ok: - point is not necessary */
  471. if (GETF_DEBUG(Ep)) printf ("ret1[%2d|%d]\n", level, GETF_ABORTCOMMAND(Ep));
  472.  
  473.     --level;
  474.  
  475.     while (--repi >= 0)
  476.     free (repstr[repi]);
  477.  
  478.     // if (aux1) free (aux1); /* PATCH_BREAK - is not necessary */
  479.  
  480.     return(1);
  481. } /* do_command */
  482.  
  483.  
  484. /*DEFHELP #cmd misc NULL - no operation */
  485. /*DEFHELP #cmd misc NOP - no operation */
  486. /*DEFHELP #cmd misc REM com - add commend */
  487.  
  488. DEFUSERCMD("nop",  0, CF_VWM|CF_ICO|CF_COK,void,do_null,(void),;)
  489. DEFUSERCMD("rem",  1, CF_VWM|CF_ICO|CF_COK,void,do_null,(void),;)
  490. DEFUSERCMD("null", 0, CF_VWM|CF_ICO|CF_COK,void,do_null,(void),)
  491. {
  492. } /* do_null */
  493.  
  494.  
  495. /*DEFHELP #cmd program,io SOURCE file - source a script file. '#' in first column for comment */
  496.  
  497. DEFUSERCMD("source", 1, CF_VWM|CF_COK|CF_ICO, void, do_source, (long do_err),)
  498. {
  499.     char   buf[MAXLINELEN];
  500.     FILE * fi;
  501.     char * str;
  502.     BPTR   oldlock = CurrentDir(DupLock(Ep->dirlock));
  503.  
  504.     if (fi = fopen(av[1], "r"))
  505.     {
  506.     while (fgets(buf, MAXLINELEN, fi))
  507.     {
  508.         if (buf[0] == '#')
  509.         continue;
  510.         for (str = buf; *str; ++str)
  511.         {
  512.         if (*str == 9)
  513.             *str = ' ';
  514.         }
  515.  
  516.         if (str > buf && str[-1] == '\n')
  517.         str[-1] = 0;
  518.  
  519.         do_command(buf);
  520.  
  521.         if (GETF_SOURCEBREAKS(Ep))                                   /* PATCH_NULL */
  522.         {
  523.         if (GETF_ABORTCOMMAND(Ep) || LoopBreak || LoopCont)      /* PATCH_NULL */
  524.         {
  525.             break;                    /* PATCH_NULL */
  526.         } /* if */                    /* PATCH_NULL */
  527.         } else                        /* PATCH_NULL */
  528.         {
  529.         SETF_ABORTCOMMAND(Ep,0);            /* PATCH_NULL */
  530.         LoopBreak    = 0;            /* PATCH_NULL */
  531.         LoopCont     = 0;            /* PATCH_NULL */
  532.         } /* if */                        /* PATCH_NULL */
  533.     }
  534.     fclose(fi);
  535.     } else
  536.     {
  537.     if (do_err)
  538.     {
  539. DEFMESSAGE( __file_not_found, "%s:\nFile `%s'\nnot found" )
  540.         error (__file_not_found, av[0], av[1]);
  541.     }
  542.     }
  543.  
  544.     UnLock(CurrentDir(oldlock));
  545. } /* do_source */
  546.  
  547.  
  548. /*DEFHELP #cmd win QUIT - close current window. If text was modified, a safety check is performed */
  549.  
  550. DEFUSERCMD("quit", 0, CF_VWM|CF_ICO, void, do_quit, (void),)
  551. {
  552.     SETF_QUITFLAG(Ep,1);
  553.  
  554.     ActivateWindow (Ep->win);                    /* HD damit aus AREXX */
  555. } /* do_quit */
  556.  
  557.  
  558. /*DEFHELP #cmd misc QUITALL - leave XDME. If any text was modified, a safety check is performed for that text */
  559.  
  560. DEFUSERCMD("quitall", 0, CF_VWM|CF_ICO, void, do_quitall, (void),)
  561. {
  562.     SETF_QUITFLAG(Ep,1);
  563.     SETF_QUITALL(Ep,1);
  564.  
  565.     ActivateWindow (Ep->win);                    /* HD damit aus AREXX */
  566. } /* do_quitall */
  567.  
  568.  
  569. /*DEFHELP #cmd misc,io,program EXECUTE comm - Execute a CLI command. */
  570.  
  571. DEFUSERCMD("execute", 1, CF_VWM|CF_ICO, void, do_execute, (void),)
  572. {
  573.     BPTR   oldlock = CurrentDir (Ep->dirlock);
  574.     BPTR   NilFH;
  575.     PROC * proc    = (PROC *)FindTask (NULL);
  576.  
  577.     NilFH = Open ("NIL:", 1006);
  578.  
  579.     if (NilFH)
  580.     {
  581.     void *oldConsoleTask = proc->pr_ConsoleTask;
  582.  
  583.     proc->pr_ConsoleTask = (APTR)BTOCP(NilFH, struct FileHandle *)->fh_Port;
  584.  
  585.     Execute (av[1], NilFH, NilFH);
  586.  
  587.     proc->pr_ConsoleTask = oldConsoleTask;
  588.  
  589.     Close(NilFH);
  590.     } else
  591.     {
  592. DEFMESSAGE( __nil_device_required, "%s:\nNIL:-device required" )
  593.     error (__nil_device_required, av[0]);
  594.     }
  595.  
  596.     CurrentDir(oldlock);
  597. } /* do_execute */
  598.  
  599.  
  600. /*
  601.  *  BREAKOUT()
  602.  *
  603.  *  Break out the next argument.  The argument is space delimited and
  604.  *  might be quoted with `' or (), or single quoted as 'c or )c
  605.  *
  606.  *  Also:    $var        -variable insertion
  607.  *        ^c        -control character
  608.  */
  609.  
  610. Prototype char * breakout (char ** ptr, char * quoted, char ** paux);
  611.  
  612. char * breakout (char ** ptr, char * quoted, char ** paux)
  613. {
  614.     char * str = *ptr;
  615.     char * base;
  616.     WORD   count;        /* Level of brace */
  617.     char   opc;         /* Open-Char */
  618.     char   clc;         /* Close-Char */
  619.     char   immode;
  620.     char   isaux;
  621.     char   buf[MAXLINELEN];
  622.     WORD   di, i;
  623.  
  624. /* if (GETF_DEBUG(Ep)) printf("bout[  |%d] ? %s\n", GETF_ABORTCOMMAND(Ep), str); */
  625.  
  626.     count =
  627.     opc   =
  628.     clc   =
  629.     immode=
  630.     isaux =
  631.     di    = 0;
  632.  
  633.     *quoted = 0;
  634.     if (paux)
  635.     *paux = NULL;
  636.  
  637.     while (*str == ' ')
  638.     ++str;
  639.  
  640.     if (!*str)
  641.     return (NULL);
  642.  
  643.     *ptr = str;
  644.     base = str;
  645.  
  646.     while (*str)
  647.     {
  648.     if (immode)
  649.     {
  650.         if (di != sizeof(buf)-1)
  651.         buf[di++] = *str;
  652.  
  653.         str ++;
  654.         continue;
  655.     }
  656.  
  657.     if (count == 0)
  658.     {
  659.         if (*str == ' ')
  660.         break;
  661.  
  662.         if (*str == '\'' || *str == ')')
  663.         clc = *str;
  664.         else if (*str == '`')
  665.         {
  666.         opc = '`';
  667.         clc = '\'';
  668.         } else if (*str == '(')
  669.         {
  670.         opc = '(';
  671.         clc = ')';
  672.         }
  673.     }
  674.  
  675.     if (*str == opc)
  676.     {
  677.         count ++;
  678.  
  679.         if (str == *ptr)
  680.         {
  681.         *quoted = 1;
  682.         base = ++ str;
  683.         continue;
  684.         }
  685.     }
  686.  
  687.     if (*str == clc)
  688.     {
  689.         count --;
  690.  
  691.         if (count == 0 && *quoted)     /*  end of argument     */
  692.         break;
  693.  
  694.         if (str == *ptr && count < 0)
  695.         {
  696.         immode = 1;
  697.         *quoted = 1;
  698.         base = ++str;
  699.  
  700.         continue;
  701.         }
  702.     }
  703.  
  704.     /*
  705.      *  $varname $(varname) $`varname'.  I.E. three forms are allowed,
  706.      *  which allows one to insert the string almost anywhere.  The
  707.      *  first form names are limited to alpha-numerics, '-', and '_'.
  708.      */
  709.  
  710.     if (*str == '$')
  711.     {
  712.         char * ptr2;
  713.         char * tmpptr;
  714.         char   c,
  715.            ce;
  716.         WORD   len;
  717.  
  718. /* if (GETF_DEBUG(Ep)) printf("bout[  |%d] $ %s\n", GETF_ABORTCOMMAND(Ep), str); */
  719.  
  720.         ce = 0;                /*    first form  */
  721.         str ++;                /*    skip $        */
  722.  
  723.         if (*str == '(')                /*  second form */
  724.         {
  725.         ce = ')';
  726.         ++str;
  727.         } else if (*str == '`')         /*  third form  */
  728.         {
  729.         ce = '\'';
  730.         ++str;
  731.         }
  732.  
  733.         ptr2 = str;             /*    start of varname    */
  734.  
  735.         if (ce)                         /*  until end char OR   */
  736.         {
  737.         while (*ptr2 && *ptr2 != ce)
  738.             ptr2 ++;
  739.         } else                /*    smart end-varname   */
  740.         {
  741.         while (isalnum(*ptr2) || *ptr2 == '-' || *ptr2 == '_' )
  742.         {
  743.             ptr2 ++;
  744.         }
  745.         }
  746.  
  747.         len = ptr2 - str;            /*    length of variable  */
  748.         c = *ptr2; *ptr2 = 0;        /*    temp. terminate \0  */
  749.  
  750. /* if (GETF_DEBUG(Ep)) printf("bout[  |%d] var '%s'\n", GETF_ABORTCOMMAND(Ep), str); */
  751.  
  752.         if (stricmp(str, "path") == 0)
  753.         {
  754.         *ptr2 = c;
  755.         isaux = 1;
  756.  
  757.         if (NameFromLock(Ep->dirlock, tmp_buffer, sizeof(tmp_buffer)))
  758.         {
  759.             i = strlen (tmp_buffer);
  760.  
  761.             if (di + i < sizeof(buf)-1)
  762.             {
  763.             strcpy (buf+di, tmp_buffer);
  764.             di += i;
  765.             }
  766.         }
  767.  
  768.         str += len;
  769.         if (ce)
  770.             ++str;
  771.  
  772.         continue;
  773.         } else
  774.         if (stricmp(str, "file") == 0)
  775.         {
  776.         i = strlen(Ep->name);
  777.         *ptr2 = c;
  778.         isaux = 1;
  779.         if (di + i < sizeof(buf)-1)
  780.         {
  781.             movmem(Ep->name, buf + di, i);
  782.             di += i;
  783.             buf[di] = 0;
  784.         }
  785.         str += len;
  786.         if (ce)
  787.             ++str;
  788.         continue;
  789. #if 0 /* the following variables are part of SPC.PRE */
  790.         } else
  791.         if (stricmp(str, "currentdir") == 0)
  792.         {
  793.         *ptr2  = c;
  794.         isaux = 1;
  795.  
  796.         if (NameFromLock (Ep->dirlock, tmp_buffer, sizeof(tmp_buffer)))
  797.         {
  798.             i = strlen(tmp_buffer);
  799.  
  800.             if (di + i < sizeof(buf)-1)
  801.             {
  802.             strcpy (buf + di, tmp_buffer);
  803.             di += i;
  804.             }
  805.         }
  806.  
  807.         str += len;
  808.  
  809.         if (ce)
  810.             ++str;
  811.  
  812.         continue;
  813.         } else
  814.         if (stricmp(str, "filename") == 0)
  815.         {
  816.         *ptr2 = c;
  817.         isaux = 1;
  818.  
  819.         if (NameFromLock(Ep->dirlock, tmp_buffer, sizeof(tmp_buffer)))
  820.         {
  821.             if (AddPart (tmp_buffer, Ep->name, sizeof(tmp_buffer)))
  822.             {
  823.             i = strlen (tmp_buffer);
  824.  
  825.             if (di + i < sizeof(buf)-1)
  826.             {
  827.                 strcpy(buf + di, tmp_buffer);
  828.                 di += i;
  829.             }
  830.             }
  831.         }
  832.  
  833.         str += len;
  834.         if (ce)
  835.             ++str;
  836.         continue;
  837.         } else if (!stricmp(str, "rexxport"))           /* TJM */
  838.         {
  839.         *ptr2 = c;
  840.         isaux = 1;
  841.  
  842.         if (di + strlen(RexxPortName) < sizeof(buf)-1)
  843.         {
  844.             strcpy(buf + di, RexxPortName);
  845.             di += strlen(buf + di);
  846.         }
  847.  
  848.         str += len;
  849.  
  850.         if (ce)
  851.             ++str;
  852.         continue;
  853.         } else if (!stricmp(str, "rxresult"))
  854.         {
  855.         *ptr2 = c;
  856.         isaux = 1;
  857.  
  858.         if (di + strlen(get_rexx_result()) < sizeof(buf)-1)
  859.         {
  860.             strcpy(buf + di, get_rexx_result());
  861.             di += strlen(buf + di);
  862.         }
  863.  
  864.         str += len;
  865.  
  866.         if (ce)
  867.             ++str;
  868.         continue;
  869. #endif
  870.         } else if (tmpptr = getvar(str))
  871.         {
  872.         ptr2 = tmpptr;
  873.         str[len] = c;
  874.         isaux = 1;
  875.  
  876.         if (di + strlen(ptr2) < sizeof(buf)-1)
  877.         {
  878.             strcpy(buf + di, ptr2);
  879.             di += strlen(buf + di);
  880.         }
  881.  
  882.         str += len;
  883.  
  884.         if (ce)
  885.             ++str;
  886.  
  887.         free(ptr2);
  888.         continue;
  889.         }
  890.  
  891.         *ptr2 = c;
  892.         str --;
  893.  
  894.         if (ce)
  895.         str --;
  896.     } else if (*str == '^' && (str[1] & 0x1F))   /* CTRL-sequence */
  897.     {
  898.         str ++;
  899.         *str &= 0x1F;
  900.         isaux = 1;
  901.     } else if (*str == '\\' && str[1])
  902.     {
  903.         /* reverted to old code */
  904. #ifdef NOTDEF
  905.         if (!count) /* ignore \ */
  906.         {
  907.         str ++;
  908.         } else if (*quoted && count == 1) /* remove it, but check next char */
  909.         {
  910. #endif
  911.         str ++;
  912. #ifdef NOTDEF
  913.         isaux = 1;
  914.         continue;
  915.         } else /* don't remove it, but also ignore next char */
  916.         {
  917.         buf[di ++] = *str ++;
  918.         }
  919. #endif
  920.         isaux = 1;
  921.     }
  922.  
  923.     buf[di ++] = *str ++;
  924.     } /* while (*str) */
  925.  
  926.     buf[di ++] = 0;
  927.  
  928.     if (isaux)
  929.     {
  930.     *paux = malloc (di);
  931.     strcpy (*paux, buf);
  932.     base = *paux;
  933.     }
  934.  
  935.     if (*str)               /*  space ended */
  936.     {
  937.     *str = '\0';
  938.     *ptr = str + 1;     /*    next arg    */
  939.     } else
  940.     {
  941.     *ptr = str;        /*    last arg    */
  942.     }
  943.  
  944.     return (base);
  945. } /* breakout */
  946.  
  947.  
  948. /******************************************************************************
  949. *****  ENDE command.c
  950. ******************************************************************************/
  951.  
  952. /*DEFHELP #cmd special BREAKOUT - Some words about Variable Expansion */
  953. /*DEFLONG #long BREAKOUT
  954.  
  955. It seems that some words must be said to variable expansion ...
  956. the bad thing is, I have not written the function, that's doing the
  957. expansion, so i cannot guarantee, that the following text is absolutely
  958. correct ...
  959.  
  960. The current Version of XDME's macro interpreter expands variables in
  961. preparation of command calls; furthermore, the macro language does not
  962. know anything else than commands ... (ok, there are other things
  963. than only commands: abbreviated commands (quoted text for write,
  964. and numbers for repeat ...), macros (which are treated like commands)
  965. and ARexx, but these themes are not discussed here ...)
  966. even "constructs" like IF, WHILE, REPEAT are commands, and so they
  967. have also arguments ... and there is no difference in the evaluation
  968. of their arguments compared to other commands ...
  969.  
  970. (in the following section we precede ecah example line with "%"
  971.  and we use a non-existing command called "out", so the following
  972.  2 macrodefinitions are useful when testing the examples ...
  973.  the first one just ignores the leading "%" and the second displays
  974.  its argument in the next line; the mapping one lets XDME
  975.  send the current line to its macro interpreter)
  976.     % setmacro %   0 ()
  977.     % setmacro out 1 (firstnb down insline tab (-> \$arg1) title OK)
  978.     % map a-a (eval \$currentline)
  979.  
  980. let us suppose we had done the following variable assignments ...
  981.     % set alpha xx
  982.     % set cmp[1]  Amiga
  983.     % set cmp[2]  Atari
  984.     % set cmp[3]  Clone
  985.     % set best      1
  986.     % set quality best
  987.  
  988. * A Variable name may contain only alphanumeric chars and/or "-", "_"
  989.   if You wanna use other charcters inside a variablename, it must be
  990.   enclosed with parantheses or Single Quotation marks ( "(...)" or
  991.   "`...'" both ways are called 'quotes' in the next paragraphes).
  992.  
  993. * The Interpreter currently has knowledge of something like 4 classes
  994.   of characters, that are alphanumeric chars together with "-" and "_"
  995.   which make up continous blocks of text, whitespace (for XDME this is
  996.   always SPACE (0x20), since TAB (0x07) ist translated to 0x20 when
  997.   reading files ...) Special characters ( "\\", "(", ")", "`", "'" and
  998.   "$" ) and all other chracters;
  999.   as long as no special character is invloved, we can say Whitespace
  1000.   is used as delimiter for arguments;
  1001.  
  1002.     % out ah.that/is*very;interesting
  1003.     -> ah.that/is*very;interesting
  1004.  
  1005.   as soon as special charcters are involved the situation gets hairy ...
  1006.   - "\\" are ignored, instead the nex caracter looses all of its meanings
  1007.     and is just copied to the current argument ('escaping')
  1008.     so we can say
  1009.  
  1010.     % out now\\ we\\ build\\ a\\ long\\ string\\ \\w/\\ whitespace\\ and\\ "\\$x"
  1011.     -> now we build a long string w/ whitespace and "$x"
  1012.  
  1013.     in order to get a "\\", that caharcter must actually be doubled
  1014.  
  1015.     % out \\\\
  1016.     -> \\
  1017.  
  1018.   - "$" introduces the next variable; as stated above, a variable
  1019.     name may contain only alphanumeric chars and/or "-", "_", else
  1020.     it must be enclosed w/ quotes
  1021.  
  1022.     % out $quality
  1023.     -> best
  1024.     % out $(cmp[1])
  1025.     -> Amiga
  1026.     % out $cmp[1]
  1027.     -> $cmp[1]
  1028.     (assuming 'cmp' in an unset variable))
  1029.  
  1030.     lonely "$" or sequences of "$" like "$$" will probably disturb the
  1031.     variable expansion, (try to expand a variable of no name) so the
  1032.     following macro might break ("might" since this behaviour might be
  1033.     changed one day)...
  1034.  
  1035.     % out $$
  1036.     (probably no output ...)
  1037.  
  1038.   - "(" and ")" as well as "`" and "'" can disable the whitespace
  1039.     argument splitting ... ( "quoting" )
  1040.  
  1041.     % out (hey, now w/out espaces)
  1042.     -> hey, now w/out espaces
  1043.  
  1044.     these quotes can also be stacked, but inside of "(/)" "`/'" will
  1045.     loose their meaning and vice versa
  1046.  
  1047.     % eval (out ( hello ( hahah ) ` )) out ( ' hohoho )
  1048.     ->  hello ( hahah ) `
  1049.     ->  ' hohoho
  1050.  
  1051.     if a open-quote has no conterpart, it quotes the complete rest of the
  1052.     current string
  1053.  
  1054.     % out ( sim sala
  1055.     ->  sim sala
  1056.  
  1057.     if an close-quote has no counter part, the same as above;
  1058.     the tricky thing is: a leading close quote cannot have a
  1059.     matching open-quote
  1060.  
  1061.     % out ) now we can type whatever we want ... ( ` ' ' )
  1062.     ->  now we can type whatever we want ... ( ` ' ' )
  1063.  
  1064. * If a variable is unknown to the system, or it cannot be resoved due
  1065.   to other reasons, e.g. to low memory conditions, it is expanded to
  1066.   itself; additionally it may happen, that the variable Module also
  1067.   sets the abortflag (this behaviour it currently not defined, so
  1068.   it might depend on the variablename being used)
  1069.  
  1070. * You can ask, if a variable exists by preceeding its name with a
  1071.   questionmark ( "?" )
  1072.   so in the above example we could call
  1073.     % out $(?alpha)
  1074.     -> 1
  1075.     (we need quotes, since a questionmark is else treated
  1076.     as a breaking (non-alnum) character ...)
  1077.  
  1078. * if You want to expand nested variables, You must reinvoke the
  1079.   interpreter; for that purpose, You can use the command EVAL;
  1080.   please note, that - as stated above - also IF and WHILE
  1081.   are commands; for that resaon, it might be neccessary to put
  1082.   a lot of esacpes in highly nested macros ...
  1083.  
  1084.     % out ( the $(cmp[$$quality]) is the $quality )
  1085.     ->  the $(cmp[$best]) is the best
  1086.     (probably no output, see above ...)
  1087.  
  1088.     % eval (out ( the \$(cmp[\$$quality]) is the $quality ))
  1089.     ->  the $(cmp[1]) is the best
  1090.  
  1091.     % eval (eval (out ( the \\\$(cmp[\$$quality]) is the $quality )))
  1092.     ->  the Amiga is the best
  1093.  
  1094.  
  1095. Additional comment: when using AmigaGuide (v34) the above text
  1096.     may have many duplicated backslashes; this is caused by the
  1097.     fact that Multiview (v39f) does treat the backslash as a
  1098.     special character, so I had the choose between using single
  1099.     backslashes for AmigaGuide v34 which then are invisible for
  1100.     Mulitiview, or using them duplicated for v34 what is the right
  1101.     way for Multiview ...
  1102.  
  1103. */
  1104.